package cn.com.shopec.erp.product.service.impl;

import java.util.ArrayList;
import java.util.Date;
import java.util.HashSet;
import java.util.List;
import java.util.Set;

import javax.annotation.Resource;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.hibernate.Query;
import org.hibernate.criterion.DetachedCriteria;
import org.hibernate.criterion.MatchMode;
import org.hibernate.criterion.Restrictions;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import cn.com.shopec.erp.common.constant.SZSCMConstant;
import cn.com.shopec.erp.common.page.PageFinder;
import cn.com.shopec.erp.common.page.PageQuery;
import cn.com.shopec.erp.common.utils.DateUtil;
import cn.com.shopec.erp.common.utils.LogicUtil;
import cn.com.shopec.erp.common.utils.StringUtil;
import cn.com.shopec.erp.product.commo.SpecPropertyUtils;
import cn.com.shopec.erp.product.dao.ICommodityCatDao;
import cn.com.shopec.erp.product.dao.ICommodityDao;
import cn.com.shopec.erp.product.dao.IProdSpecValueDao;
import cn.com.shopec.erp.product.dao.IProductDao;
import cn.com.shopec.erp.product.dao.ISpecValOptionDao;
import cn.com.shopec.erp.product.dao.ISpecificationDao;
import cn.com.shopec.erp.product.entity.Commodity;
import cn.com.shopec.erp.product.entity.CommodityCat;
import cn.com.shopec.erp.product.entity.Product;
import cn.com.shopec.erp.product.entity.SpecValOption;
import cn.com.shopec.erp.product.entity.Specification;
import cn.com.shopec.erp.product.model.SpecValOptionInfo;
import cn.com.shopec.erp.product.model.SpecificationDetail;
import cn.com.shopec.erp.product.model.SpecificationInfo;
import cn.com.shopec.erp.product.service.ISpecificationService;
import cn.com.shopec.erp.product.vo.CommodityCatVo;

/**
 * @ClassName: SpecificationServiceImpl
 * @Description: 货品规格相关的service层接口实现
 * @author: lule
 * @date 2013-5-28 下午7:41:19
 * 
 */
@Service
public class SpecificationServiceImpl implements ISpecificationService {

    private static final Log log = LogFactory.getLog(SpecificationServiceImpl.class);

    @Resource
    private ISpecificationDao specDao;

    @Resource
    private ISpecValOptionDao optionDao;

    @Resource
    private IProductDao prodDao;

    @Resource
    private ICommodityDao commoDao;

    @Resource
    private IProdSpecValueDao prodSpecValueDao;
    
    @Resource
    private ICommodityCatDao catDao;
    
    

    /*
     * (no-Javadoc) <p>Title: getSpecifications</p> <p>Description: </p>
     * 
     * @param catNo
     * 
     * @return
     * 
     * @throws Exception
     * 
     * @see
     * cn.com.shopec.erp.product.service.ISpecificationService#getSpecifications
     * (java.lang.String)
     */
    @Override
    @Transactional
    public List<SpecificationInfo> getSpecifications(String catNo) throws Exception {
        List<Specification> specList = null;

        List<SpecificationInfo> specificationList = null;
        try {
            DetachedCriteria criteria = this.specDao.createCriteria();
            if (StringUtil.isExist(catNo)) {
                criteria.add(Restrictions.eq("catNo", catNo));
            }
            criteria.add(Restrictions.eq("deleteFlag", SZSCMConstant.DELETE_FLAG_NO));
            specList = this.specDao.findByCriteria(criteria);

            List<SpecificationInfo> specificationInfoList = SpecPropertyUtils.getSpecificationInfoList(specList);
            if (LogicUtil.isNotNullAndEmpty(specificationInfoList)) {
                specificationList = new ArrayList<SpecificationInfo>();
                for (SpecificationInfo specificationInfo : specificationInfoList) {
                    SpecificationDetail specificationDetail = this
                            .getSpecificationDetail(specificationInfo.getSpecNo());
                    specificationInfo.setSpecificationDetail(specificationDetail);

                    specificationList.add(specificationInfo);
                }
            }

        } catch (Exception e) {
            log.error(e.getMessage(), e);
            throw e;
        }

        return specificationList;
    }

    /*
     * (no-Javadoc) <p>Title: getPagedSpecifications</p> <p>Description: </p>
     * 
     * @param catNo
     * 
     * @param pageQuery
     * 
     * @return
     * 
     * @throws Exception
     * 
     * @see cn.com.shopec.erp.product.service.ISpecificationService#
     * getPagedSpecifications(java.lang.String,
     * cn.com.shopec.erp.common.page.PageQuery)
     */
    @Override
    @Transactional
    public PageFinder<SpecificationInfo> getPagedSpecifications(SpecificationInfo specification, PageQuery pageQuery)
            throws Exception {
        PageFinder<SpecificationInfo> retPage = null;
        try {
            DetachedCriteria criteria = this.specDao.createCriteria();
             
            if (LogicUtil.isNotNull(specification)) {
                if (LogicUtil.isNotNullAndEmpty(specification.getSpecName())) {
                    criteria.add(Restrictions.like("specName", specification.getSpecName().trim(), MatchMode.ANYWHERE));
                }
                if (StringUtil.isExist(specification.getCatNo()) && !"0".equals(specification.getCatNo())) {
                    criteria.add(Restrictions.eq("catNo", specification.getCatNo()));
                }
            }
            criteria.add(Restrictions.eq("deleteFlag", SZSCMConstant.DELETE_FLAG_NO));
            PageFinder<Specification> page = this.specDao.pagedByCriteria(criteria, pageQuery.getPage(),
                    pageQuery.getPageSize());
            List<SpecificationInfo> specificationInfos=SpecPropertyUtils.getSpecificationInfoList(page.getData());
            
            List<SpecificationInfo> specificationInfoList=null;
            if(LogicUtil.isNotNullAndEmpty(specificationInfos)){
            	specificationInfoList=new ArrayList<SpecificationInfo>();
            	
            	for(SpecificationInfo specInfo:specificationInfos){
            		CommodityCat commodityCat = this.catDao.findUniqueBy("catNo",specInfo.getCatNo());
            		specInfo.setCatName(null ==commodityCat?"":commodityCat.getCatName());
            		specificationInfoList.add(specInfo);
            	}
            }

            retPage = new PageFinder<SpecificationInfo>(page.getPageNo(), page.getPageSize(), page.getRowCount(),
            		specificationInfoList);

        } catch (Exception e) {
            log.error(e.getMessage(), e);
            throw e;
        }

        return retPage;
    }

    /*
     * (no-Javadoc) <p>Title: getSpecificationDetail</p> <p>Description: </p>
     * 
     * @param specNo
     * 
     * @return
     * 
     * @throws Exception
     * 
     * @see cn.com.shopec.erp.product.service.ISpecificationService#
     * getSpecificationDetail(java.lang.String)
     */
    @Override
    public SpecificationDetail getSpecificationDetail(String specNo) throws Exception {
        if (StringUtil.isBlank(specNo)) {
            throw new IllegalArgumentException("Argument [specNo] is illegal.");
        }
        SpecificationDetail detail = null;

        try {
            Specification spec = this.specDao.get("specNo", specNo);
            if (spec != null && spec.getDeleteFlag() != SZSCMConstant.DELETE_FLAG_YES) {
                detail = SpecPropertyUtils.getSpecificationDetail(spec);
            }
        } catch (Exception e) {
            log.error(e.getMessage(), e);
            throw e;
        }

        return detail;
    }

    /*
     * (no-Javadoc) <p>Title: addSpecificationDetail</p> <p>Description: </p>
     * 
     * @param detail
     * 
     * @return
     * 
     * @throws Exception
     * 
     * @see cn.com.shopec.erp.product.service.ISpecificationService#
     * addSpecificationDetail
     * (cn.com.shopec.erp.product.model.SpecificationDetail)
     */
    @Override
    @Transactional
    public int addSpecificationDetail(SpecificationDetail detail) throws Exception {
        int res = SZSCMConstant.FAIL_INT;
        if (detail != null) {
            try {
                // 先添加规格
                Specification spec = SpecPropertyUtils.getSpecification(detail);
                this.specDao.save(spec);

                // 再添加规格下的选项
                List<SpecValOption> options = SpecPropertyUtils.getSpecValOptionList(detail.getSpecValOptions());
                for (SpecValOption option : options) {
                    this.optionDao.save(option);
                }

                res = SZSCMConstant.SUCCESS_INT;
            } catch (Exception e) {
                log.error(e.getMessage(), e);
                throw e;
            }
        }
        return res;
    }

    /*
     * (no-Javadoc) <p>Title: saveSpecification</p> <p>Description: </p>
     * 
     * @param info
     * 
     * @return
     * 
     * @throws Exception
     * 
     * @see
     * cn.com.shopec.erp.product.service.ISpecificationService#saveSpecification
     * (cn.com.shopec.erp.product.model.SpecificationInfo)
     */
    @Override
    @Transactional
    public int saveSpecification(SpecificationInfo info) throws Exception {
        int res = SZSCMConstant.FAIL_INT;
        if (info != null) {
            if (StringUtil.isBlank(info.getCatNo()) || StringUtil.isBlank(info.getSpecName())) {
                res = -1; // catNo为空或specName为空
                return res;
            }

            boolean isAdd = StringUtil.isBlank(info.getSpecNo());

            try {
                Specification spec = null;
                if (isAdd) { // 新增规格
                    spec = SpecPropertyUtils.getSpecification(info);
                    spec.setSpecNo(SpecPropertyUtils.genSpecificationNo());

                    this.specDao.save(spec);

                    res = SZSCMConstant.SUCCESS_INT;
                } else { // 更新规格
                    spec = this.specDao.get("specNo", info.getSpecNo());
                    if (spec != null) {
                        spec.setSpecName(info.getSpecName());
                        spec.setUpdateTime(DateUtil.formatDateByFormat(new Date(), DateUtil.TIMESTAMP_PATTERN));

                        this.specDao.update(spec);

                        // 更新与该规格相关的货品、及商品的updateTime字段
                        if (this.updateProductAndCommodityAfterUpdatingSpec(spec.getSpecNo()) != SZSCMConstant.SUCCESS_INT) {
                            res = -3;
                        } else {
                            res = SZSCMConstant.SUCCESS_INT;

                        }
                    } else {
                        res = -2;
                    }

                }

                if (isAdd && res == SZSCMConstant.SUCCESS_INT) { // 如果新增成功时，则设置参数info的specNo值
                    info.setSpecNo(spec.getSpecNo());
                }
            } catch (Exception e) {
                log.error(e.getMessage(), e);
                throw e;
            }
        }
        return res;
    }

    /*
     * (no-Javadoc) <p>Title: delSpecification</p> <p>Description: </p>
     * 
     * @param specNo
     * 
     * @return
     * 
     * @throws Exception
     * 
     * @see
     * cn.com.shopec.erp.product.service.ISpecificationService#delSpecification
     * (java.lang.String)
     */
    @Override
    @Transactional
    public int delSpecification(String specNo) throws Exception {
        int res = SZSCMConstant.FAIL_INT;
        if (StringUtil.isBlank(specNo)) {
            throw new IllegalArgumentException("Argument [specNo] can't be null.");
        }
        specNo = specNo.trim();
        try {
            // 先检查该规格是否可以删除
            boolean isBeUsing = this.isSpecificationBeUsing(specNo);
            if (isBeUsing) {
                res = -2;
            } else {
                Specification spec = this.specDao.get("specNo", specNo);
                if (spec != null) {
                    DetachedCriteria criteria = this.optionDao.createCriteria();
                    criteria.add(Restrictions.eq("specNo", specNo));
                    List<SpecValOption> options = this.optionDao.findByCriteria(criteria);

                    String updateTime = DateUtil.formatDateByFormat(new Date(), DateUtil.TIMESTAMP_PATTERN);
                    spec.setDeleteFlag(SZSCMConstant.DELETE_FLAG_YES);
                    spec.setUpdateTime(updateTime);

                    this.specDao.update(spec); // 逻辑删除规格

                    for (SpecValOption option : options) {
                        option.setDeleteFlag(SZSCMConstant.DELETE_FLAG_YES);
                        option.setUpdateTime(updateTime);
                        this.optionDao.update(option); // 逻辑删除
                    }
                    res = SZSCMConstant.SUCCESS_INT;
                } else {
                    res = -1;
                }
            }

        } catch (Exception e) {
            log.error(e.getMessage(), e);
            throw e;
        }

        return res;
    }

    /*
     * (no-Javadoc) <p>Title: getSpecValOption</p> <p>Description: </p>
     * 
     * @param optionNo
     * 
     * @return
     * 
     * @throws Exception
     * 
     * @see
     * cn.com.shopec.erp.product.service.ISpecificationService#getSpecValOption
     * (java.lang.String)
     */
    @Override
    public SpecValOptionInfo getSpecValOption(String optionNo) throws Exception {
        if (StringUtil.isBlank(optionNo)) {
            throw new IllegalArgumentException("Argument [optionNo] is illegal.");
        }
        SpecValOptionInfo o = null;
        try {
            SpecValOption option = this.optionDao.get("optionNo", optionNo.trim());
            if (option != null) {
                o = SpecPropertyUtils.getSpecValOptionInfo(option);
            }
        } catch (Exception e) {
            log.error(e.getMessage(), e);
            throw e;
        }

        return o;
    }

    /*
     * (no-Javadoc) <p>Title: saveSpecValOption</p> <p>Description: </p>
     * 
     * @param option
     * 
     * @return
     * 
     * @throws Exception
     * 
     * @see
     * cn.com.shopec.erp.product.service.ISpecificationService#saveSpecValOption
     * (cn.com.shopec.erp.product.model.SpecValOptionInfo)
     */
    @Override
    @Transactional
    public int saveSpecValOption(SpecValOptionInfo option) throws Exception {
        int res = SZSCMConstant.FAIL_INT;
        if (option != null) {
            if (StringUtil.isBlank(option.getSpecNo()) || StringUtil.isBlank(option.getOptionName())) {
                res = -1; // specNo或optionName为空
                return res;
            }

            boolean isAdd = StringUtil.isBlank(option.getOptionNo());

            try {
                SpecValOption o = null;
                if (isAdd) { // 新增选项
                    o = SpecPropertyUtils.getSpecValOption(option);
                    o.setOptionNo(SpecPropertyUtils.genSpecValOptionNo());

                    this.optionDao.save(o);
                    res = SZSCMConstant.SUCCESS_INT;
                } else { // 更新选项
                    o = this.optionDao.get("optionNo", option.getOptionNo());
                    if (o != null) {
                        o.setOptionName(option.getOptionName());
                        o.setUpdateTime(DateUtil.formatDateByFormat(new Date(), DateUtil.TIMESTAMP_PATTERN));

                        this.optionDao.update(o);
                        // 更新与该规格选项值相关的货品、及商品的updateTime字段
                        if (this.updateProductAndCommodityAfterUpdatingSpecValOption(o.getOptionNo()) != SZSCMConstant.SUCCESS_INT) {
                            res = -3;
                        } else {
                            res = SZSCMConstant.SUCCESS_INT;

                        }
                    } else {
                        res = -2;
                    }

                }

                if (isAdd && res == SZSCMConstant.SUCCESS_INT) { // 如果是新增成功的，则设置参数option的optionNo属性值
                    option.setOptionNo(o.getOptionNo());
                }
            } catch (Exception e) {
                log.error(e.getMessage(), e);
                throw e;
            }
        }

        return res;
    }

    /*
     * (no-Javadoc) <p>Title: delSpecValOption</p> <p>Description: </p>
     * 
     * @param optionNo
     * 
     * @return
     * 
     * @throws Exception
     * 
     * @see
     * cn.com.shopec.erp.product.service.ISpecificationService#delSpecValOption
     * (java.lang.String)
     */
    @Override
    @Transactional
    public int delSpecValOption(String optionNo) throws Exception {
        int res = SZSCMConstant.FAIL_INT;
        if (StringUtil.isBlank(optionNo)) {
            throw new IllegalArgumentException("Argument [optionNo] can't be null.");
        }
        optionNo = optionNo.trim();
        try {
            // 先检查该选项是否可以删除
            boolean isBeUsing = this.isSpecValOptionBeUsing(optionNo);
            if (isBeUsing) {
                res = -2;
            } else {
                // 根据optionNo获取实体
                SpecValOption option = this.optionDao.get("optionNo", optionNo);
                if (option == null) {
                    res = -1; // 找不到
                } else {
                    option.setDeleteFlag(SZSCMConstant.DELETE_FLAG_YES);
                    option.setUpdateTime(DateUtil.formatDateByFormat(new Date(), DateUtil.TIMESTAMP_PATTERN));

                    this.optionDao.update(option);
                    res = SZSCMConstant.SUCCESS_INT;
                }
            }

        } catch (Exception e) {
            log.error(e.getMessage(), e);
            throw e;
        }

        return res;
    }

    /**
     * @Title: updateProductAndCommodityAfterUpdatingSpec
     * @Description: 在更新了规格（除了更新外，还包括逻辑删除）之后，更新相关货品、商品的updateTime属性
     * @param specNo
     * @return 操作结果代码（1：成功，0：失败）
     * @throws Exception
     * @throws
     */
    @Transactional
    private int updateProductAndCommodityAfterUpdatingSpec(String specNo) throws Exception {
        int res = SZSCMConstant.FAIL_INT;
        if (!StringUtil.isBlank(specNo)) {
            String hql = "select p from Product p, ProdSpecValue s where p.prodNo=s.prodNo and s.specNo=?";
            this.prodDao.findByHql(hql, specNo.trim());
            try {
                // if (prods != null && prods.size() != 0) {
                // this.updateTheUpdateTimeOfProductAndCommodity(prods);
                // }
                res = SZSCMConstant.SUCCESS_INT;
            } catch (Exception e) {
                log.error(e.getMessage(), e);
            }
        }
        return res;
    }

    /**
     * @Title: updateProductAndCommodityAfterUpdatingSpecValOption
     * @Description: 在更新了规格选项（除了更新外，还包括逻辑删除）之后，更新相关货品、商品的updateTime属性
     * @param optionNo
     * @return 操作结果代码（1：成功，0：失败）
     * @throws Exception
     * @throws
     * @author: lule
     * @date: 2013-6-2下午9:39:50
     */
    private int updateProductAndCommodityAfterUpdatingSpecValOption(String optionNo) throws Exception {
        int res = SZSCMConstant.FAIL_INT;
        if (!StringUtil.isBlank(optionNo)) {
            String hql = "select p from Product p, ProdSpecValue s where p.prodNo=s.prodNo and s.valOptionNo=?";
            List<Product> prods = this.prodDao.findByHql(hql, optionNo.trim());
            try {
                if (prods != null && prods.size() != 0) {
                    // this.updateTheUpdateTimeOfProductAndCommodity(prods);
                }
                res = SZSCMConstant.SUCCESS_INT;
            } catch (Exception e) {
                log.error(e.getMessage(), e);
            }
        }
        return res;
    }

    /**
     * @Title: updateTheUpdateTimeOfProductAndCommodity
     * @Description: 更新货品、商品的updateTime属性
     * @param prods
     * @throws Exception
     * @throws
     */
    @Transactional
    private void updateTheUpdateTimeOfProductAndCommodity(List<Product> prods) throws Exception {
        String time = DateUtil.formatDateByFormat(new Date(), DateUtil.TIMESTAMP_PATTERN);
        Set<Commodity> commodities = new HashSet<Commodity>();
        for (Product prod : prods) {
            // Commodity commodity = prod.getCommodity();
            // if (commodity != null) {
            // commodities.add(commodity);
            // }

            prod.setUpdateTime(time);
            this.prodDao.update(prod);
        }
        for (Commodity commodity : commodities) {
            commodity.setUpdateTime(time);
            this.commoDao.update(commodity);
        }
    }

    /**
     * @Title: isSpecificationBeUsing
     * @Description: 判断一个规格是否正在被使用
     * @param specNo
     * @return
     * @throws Exception
     * @throws
     * @author: lule
     * @date: 2013-6-3上午10:48:38
     */
    private boolean isSpecificationBeUsing(String specNo) throws Exception {
        boolean res = false;

        Query q = this.prodSpecValueDao.createSQLQuery(
                "select 1 from dual where exists(select t.id from tbl_prod_spec_value t where t.spec_no=?)", specNo);
        Object o = q.uniqueResult();

        if (o != null && o.toString().equals("1")) {
            res = true;
        }

        return res;
    }

    /**
     * @Title: isSpecValOptionBeUsing
     * @Description: 判断一个规格选项值是否正在被使用
     * @param optionNo
     * @return
     * @throws Exception
     * @throws
     * @author: lule
     * @date: 2013-6-3上午10:50:17
     */
    private boolean isSpecValOptionBeUsing(String optionNo) throws Exception {
        boolean res = false;

        Query q = this.prodSpecValueDao.createSQLQuery(
                "select 1 from dual where exists(select t.id from tbl_prod_spec_value t where t.val_option_no=?)",
                optionNo);
        Object o = q.uniqueResult();

        if (o != null && o.toString().equals("1")) {
            res = true;
        }
        return res;
    }

	@Override
	public List<SpecificationInfo> getSpecificationsCatName(String specName) throws Exception {
        List<Specification> specList = null;

        List<SpecificationInfo> specificationList = null;
        try {
            DetachedCriteria criteria = this.specDao.createCriteria();
            if (StringUtil.isExist(specName)) {
                criteria.add(Restrictions.eq("specName", specName));
            }
            criteria.add(Restrictions.eq("deleteFlag", SZSCMConstant.DELETE_FLAG_NO));
            specList = this.specDao.findByCriteria(criteria);

            List<SpecificationInfo> specificationInfoList = SpecPropertyUtils.getSpecificationInfoList(specList);
            if (LogicUtil.isNotNullAndEmpty(specificationInfoList)) {
                specificationList = new ArrayList<SpecificationInfo>();
                for (SpecificationInfo specificationInfo : specificationInfoList) {
                    SpecificationDetail specificationDetail = this
                            .getSpecificationDetail(specificationInfo.getSpecNo());
                    specificationInfo.setSpecificationDetail(specificationDetail);

                    specificationList.add(specificationInfo);
                }
            }

        } catch (Exception e) {
            log.error(e.getMessage(), e);
            throw e;
        }

        return specificationList;
    }
}
